﻿
using System;
using System.IO;
using System.Text;
using MX.Core.Caching.Redis.Configuration;
using MX.Core.Caching.Redis.Serialization;

namespace MX.Core.Caching.Redis.Sider
{
	public class RedisWriter
	{
   


		public static bool AutoFlush { get; set; }



		public void WriteLine (Stream _stream, string str)
		{
			SAssert.ArgumentNotNull (() => str);
			var	_strBuffer = new byte[BufferConfig.EncodingBufferSize];
			var bytesNeeded = Encoding.Default.GetByteCount (str);
			if (bytesNeeded < _strBuffer.Length) {
				Encoding.Default.GetBytes (str, 0, str.Length, _strBuffer, 0);
				_stream.Write (_strBuffer, 0, bytesNeeded);
			} else {
				var buffer = Encoding.Default.GetBytes (str);
				_stream.Write (buffer, 0, buffer.Length);
			}

			writeCrLf (_stream);
		}

		public void WriteLine (Stream _stream, int num)
		{
			// TODO: Optimize number translation?
			WriteLine (_stream, num.ToString ());
		}


		public void WriteTypeChar (Stream _stream, ResponseType type)
		{
			SAssert.ArgumentSatisfy (() => type,
				v => Enum.IsDefined (typeof(ResponseType), v), "Invalid type char.");

			// assuming writebuffersize >= 1 so fitInBuffer(1) == true
			// the else case is for testing purpose
			_stream.WriteByte ((byte)type);
			flushIfAuto (_stream);
		}


		public void WriteBulk (Stream _stream, byte[] buffer)
		{
			SAssert.ArgumentNotNull (() => buffer);

			WriteBulk (_stream, buffer, 0, buffer.Length);
		}

		public void WriteBulk (Stream _stream, byte[] buffer, int offset, int count)
		{
			SAssert.ArgumentNotNull (() => buffer);

			if (!(offset == 0 && count == 0)) {
				SAssert.ArgumentBetween (() => offset, 0, buffer.Length);
				SAssert.ArgumentBetween (() => count, 0, buffer.Length + 1);
			}

			SAssert.ArgumentSatisfy (() => offset, o => o + count <= buffer.Length,
				"Offset plus count is larger than the buffer.");

			_stream.Write (buffer, offset, count);
			writeCrLf (_stream);
		}

		public void WriteBulkFrom (Stream _stream, Stream source, int count)
		{
			SAssert.ArgumentNotNull (() => source);
			SAssert.ArgumentNonNegative (() => count);

			using (var limiter = new LimitingStream (source, count)) {
				limiter.CopyTo (_stream);
				writeCrLf (_stream);

				SAssert.IsTrue (limiter.BytesLeft == 0,
					() => new InvalidOperationException ("Stream does not contains enough data."));
			}
		}

		public void WriteSerializedBulk (Stream _stream, byte[] obj, int count)
		{ 
			SAssert.ArgumentNonNegative (() => count); 
			BufferHelper.Write (obj, _stream, count);
			writeCrLf (_stream);
		}


		public void Flush (Stream _stream)
		{
			_stream.Flush ();
		}

		private void flushIfAuto (Stream _stream)
		{
			if (AutoFlush)
				Flush (_stream);
		}


		private void writeCrLf (Stream _stream)
		{
			_stream.WriteByte (0x0D);
			_stream.WriteByte (0x0A);
			flushIfAuto (_stream);
		}

	}
}
